<?php
namespace \DragonU\DB;
/**
 * DragonU DB Connector library.
 *
 * @internal
 * Copyright (c) 2009, Jacob Santos
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the DragonU nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Jacob Santos <wordpress@santosj.name>
 * @license http://www.opensource.org/licenses/bsd-license.php New BSD license
 * @package DragonU
 * @subpackage DB
 * @since 0.1
 */

/**
 * CorePress DB Connector class.
 *
 * Stores the connections to the database.
 *
 * @author Jacob Santos
 * @since 0.1
 * @package DragonU
 * @subpackage DB
 */
class Connector implements \Countable, \Iterator
{

	/**
	 * Whether to randomize which connection is used.
	 *
	 * The default is to use the primary database and just update the slaves.
	 *
	 * @since 0.1
	 * @var bool
	 */
	protected $loadBalance = false;

	/**
	 * The default database prefix.
	 *
	 * @since 0.1
	 * @var string
	 */
	protected $connection = array();

	/**
	 * Connect to the database, if constants are available.
	 *
	 * @since 0.1
	 *
	 * @param string $prefix Optional. Default prefix.
	 */
	public function __construct()
	{
		if( defined('DB_USER') && defined('DB_PASSWORD') )
		{
			if( defined('DB_HOST') && defined('DB_NAME') )
			{
				$dsn = 'mysql:host:'.DB_HOST.';dbname:'.DB_NAME.';';
				$this->add( 'primary', $dsn, DB_USER, DB_PASSWORD );
			}
			else if( defined('COREPRESS_DB_DSN') )
			{
				$this->add( 'primary', COREPRESS_DB_DSN, DB_USER, DB_PASSWORD );
			}
		}
	}

	/**
	 * Retrieve database connection.
	 *
	 * If load balancing is enabled, then it will pull from a random connection
	 * and return it. If load balancing is disabled, then the primary will
	 * always be returned.
	 *
	 * The load balancing will only work, if there are more than one connection.
	 * There isn't any reason to load balance with only one connection.
	 *
	 * @since 0.1
	 *
	 * @param string $dsn PDO DSN string for connection.
	 * @param string $user Optional. Username for connection.
	 * @param string $password Optional. Password for connection.
	 */
	public function db($dsn, $user = null, $password = null)
	{
		if( $this->loadBalance === true && count( $this->connection ) > 1 )
		{
			$max = count($this->connection) - 1;
			$num = mt_rand( 0, $max );

			return $this->connection[ $num ];
		}

		return $this->connection['primary'];
	}

	/**
	 * Adds a new connection to the list.
	 *
	 * @since 0.1
	 *
	 * @param string $name Name to reference connection.
	 * @param string $dsn PDO DSN string for connection.
	 * @param string $user Optional. Username for connection.
	 * @param string $password Optional. Password for connection.
	 */
	public function add($name, $dsn, $user = null, $password = null)
	{
		$this->connection[ $name ] = new \PDO($dsn, $user, $password);

		// Need to have exceptions to handle problems.
		$this->connection[ $name ]->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

		// Turbo setting for MySQL.
		if( $this->connection[ $name ]->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' )
		{
			$this->connection[ $name ]->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
		}
	}

	/**
	 * Removes last connection on the list.
	 *
	 * @since 0.1
	 *
	 * @param string $name Name to reference connection.
	 */
	public function remove($name)
	{
		if( isset( $this->connection[$name] ) )
			unset($this->connection[ $name ]);
	}

	/**
	 * Removes all but the primary in the connection list.
	 *
	 * @since 0.1
	 */
	public function removeAll()
	{
		$list = array_keys($this->connection);

		unset($list['primary']);

		foreach($list as $name)
		{
			unset( $this->connection[ $name ] );
		}
	}

	/**
	 * Synchronizes the slave databases with the primary.
	 *
	 * The process is to get all of the rows in the master and then retrieve all
	 * of the rows in the slaves one by one. A compare is done first in order to
	 * delete the rows that don't belong in the slave. A check based on the row
	 * count won't exactly work, because it is possible that additions were done
	 * while deleting and the total matches the slaves. The process has to be
	 * exact.
	 *
	 * Then a diff is done on the arrays in order to find which rows need to be
	 * added and updated. This process is repeated for each of the slaves. It
	 * won't scale very well, trying to sync many databases will be time
	 * consuming.
	 *
	 * @since 0.1
	 * @todo Complete functionality.
	 */
	public function sync()
	{

	}

	/**
	 * Whether to balance the load between the primary and slaves.
	 *
	 * @since 0.1
	 *
	 * @param bool $enable Optional, defaults to true. Use false to disable the load balancing.
	 */
	public function balanceLoad($enable = true)
	{
		$this->loadBalance = (bool) $enable;
	}

	/**
	 * How many connections were made.
	 *
	 * @since 0.1
	 */
	public function count()
	{
		return count($this->connection);
	}

	/**
	 * Retrieve connection name.
	 *
	 * @since 0.1
	 * @return string Connection name.
	 */
	public function key()
	{
		return key($this->connection);
	}

	/**
	 * Retrieve current connection.
	 *
	 * @since 0.1
	 * @return PDO Connection object.
	 */
	public function current()
	{
		return current($this->connection);
	}

	/**
	 * Iterate to next connection.
	 *
	 * @since 0.1
	 * @return PDO Connection object.
	 */
	public function next()
	{
		return next($this->connection);
	}

	/**
	 * Reset connection list.
	 *
	 * @since 0.1
	 */
	public function rewind()
	{
		reset($this->connection);
	}

	/**
	 * Whether list has ended.
	 *
	 * @since 0.1
	 * @return bool
	 */
	public function valid()
	{
		return ( $this->current() !== false );
	}

}